/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。
 * 
 * 欢迎到知启蒙网站（https://www.zhiqim.com）购买正版软件，知启蒙还提供许多开源框架和软件。
 * 
 * 1、本软件产品所有源代码受《中华人民共和国著作权法》和其他有关法律、法规的保护，其所有知识产权归湖南知启蒙科技有限公司所有；
 * 2、禁止复制和修改。不得复制修改、翻译或改编本软件所有源代码，或者基于本软件产品创作衍生作品；
 * 3、禁止进行逆向工程。不得对本软件的源代码进行逆向工程、反编译或试图以其他方式发现软件的源代码；
 * 4、个别授权：如需进行商业性的销售、复制、分发，包括但不限于软件销售、预装、捆绑等，必须获得知启蒙的书面授权和许可；
 * 5、保留权利：本注释未明示授权的其他一切权利仍归知启蒙所有，任何人使用其他权利时必须获得知启蒙的书面同意。
*/
package net.redxyz.repost.resource.action;

import java.io.IOException;

import net.redxyz.repost.resource.SessionUser;
import net.redxyz.repost.resource.model.SessionToken;
import net.redxyz.repost.service.dao.UserDao;
import net.redxyz.top.TaobaoService;
import net.redxyz.top.dbo.sys.SysApp;
import net.redxyz.top.dbo.user.User;
import net.redxyz.top.dbo.user.UserGift;

import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.httpd.HttpResponse;
import org.zhiqim.httpd.HttpdConstants;
import org.zhiqim.httpd.context.core.Action;
import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.httpclient.HttpResult;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.util.DateTimes;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.orm.ORM;
import org.zhiqim.orm.ZTable;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;

import com.xiyuan.smartutils.StrUtils;

/**
 * 登录
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class LoginAction implements Action, HttpdConstants {
    protected static final Log log = LogFactory.getLog(LoginAction.class);
    protected String fuwuUrl = TaobaoService.getFuwuUrl(Global.getString("taobao", "articleCode"));
    
    public void execute(HttpRequest request) throws Exception {
        HttpResponse response = request.getResponse();
        
        if (hasError(request, response)) {// 前置条件，1）如果有错误，则跳转到服务页
            return;
        }
        
        if (isTaobaoChk(request, response)) {// 前置条件，2）如果是淘宝后台发起的检查连接，跳转到服务页
            return;
        }
        
        if (Global.isTrue("project", "upgrade")) {// 前置条件，3）是否配置为升级模式
            response.sendRedirect("/upgrade.htm");
            return;
        }
        
        // 第一步，到淘宝获取授权数据
        String code = request.getParameter("code", null);
        String state = request.getParameterNoFilter("state", "");
        HttpResult result = TaobaoService.authorizationCode(code, state);
        if (!result.isSuccess()) {
            log.error("读取OAuth时失败:[responseState=" + result.getResponseStatus() + "]");
            request.setRedirect(fuwuUrl);
            return;
        }
        
        String token = result.getResponseText();
        if (Validates.isEmpty(token)) {
            log.error("读取OAuth时失败:token=" + token);
            request.setRedirect(fuwuUrl);
            return;
        }
        
        // 第二步，解析授权数据和状态数据
        SessionToken sessionToken = SessionToken.parseSessionToken(token);
        if (sessionToken.isInValid()) {
            log.error("解析淘宝参数时未找到用户编号或用户昵称");
            request.setRedirect(fuwuUrl);
            return;
        }
        
        String nick = sessionToken.getNick();
        if (sessionToken.isInValidParam()) {
            log.error("解析淘宝参数时未找到accessToken或refreshToken数据:[" + nick + "]");
            request.setRedirect(fuwuUrl);
            return;
        }
        
        // 第三步，保存或更新用户授权数据
        String datetime = TaobaoService.getToolService().getTaobaoTime();
        User user = UserDao.getUser(nick);
        boolean isCreate = false;// 是否新创建用户
        if (user == null) {// 用户第一次登录
            isCreate = true;
            user = new User();
            user.setNick(nick);
            user.setUserId(sessionToken.getUserId());
            user.setSessionId(sessionToken.getSessionId());
            user.setSessionTime(datetime);
            user.setSessionToken(sessionToken.getToken());
            user.setRefreshToken(sessionToken.getRefreshToken());
            user.setCreateTime(datetime);
            user.setUserPoint(0);
            user.setOpenUid(sessionToken.getOpenUid());
            
            // 用户第一次登录，通过状态获取版本号，后续在/refresh.htm中再作更新，注意/index.htm必须包含/refresh.htm
            user.setVersionNo(parseVersionNo(state));
            
            // 用户第一次登录，订购起始日期默认当前日期，后续在/refresh.htm中再作更新，注意/index.htm必须包含/refresh.htm
            user.setStartDate(DateTimes.getDateString());// 用户订购起始日期默认第一次登陆时间
            user.setEndDate(DateTimes.getNextDateStringBySecond(sessionToken.getExpiresIn()));// 结束日期默认授权到期日期
        }
        
        // 第四步，组织登录SessionUser
        SessionUser sessionUser = request.getSessionUser(SessionUser.class);
        if (sessionUser == null || !sessionUser.getNick().equals(nick)) {
            // 用户超时，或当前登录会话账号和原已登录会话账号不一致，重建会话用户。
            // 注：(同一账号或主子账号之间切换时允许共享以前的会话数据)
            sessionUser = new SessionUser();
            sessionUser.setUser(user);
            request.bindSessionUser(sessionUser);
            
            // 用户超时或当前登录会话账号和原已登录会话账号不一致，重授权时返回首页
            if ("w2close".equals(state))
                state = "w2close_index";
        }
        
        if (sessionToken.hasSubNick()) {
            // 子账号登录打印一下日志
            log.info("子账号登录[" + sessionToken.getSubNick() + "]");
            
            sessionUser.setHasSubNick(true);
            sessionUser.setSubNick(sessionToken.getSubNick());
            sessionUser.setSubSessionId(sessionToken.getSessionId());
            sessionUser.setSubSessionTime(datetime);
            sessionUser.setSubExpiresIn(sessionToken.getExpiresIn());
            sessionUser.setSubExpiresInW2(sessionToken.getExpiresInW2());
        }
        else {
            // 主账号登录，把得到的重授权数据更新到数据库
            user.setSessionId(sessionToken.getSessionId());
            user.setSessionTime(datetime);
            user.setSessionToken(sessionToken.getToken());
            user.setExpiresIn(sessionToken.getExpiresIn());
            user.setExpiresInR1(sessionToken.getExpiresInR1());
            user.setExpiresInR2(sessionToken.getExpiresInR2());
            user.setExpiresInW1(sessionToken.getExpiresInW1());
            user.setExpiresInW2(sessionToken.getExpiresInW2());
            user.setRefreshToken(sessionToken.getRefreshToken());
            user.setReExpiresIn(sessionToken.getReExpiresIn());
          
            
            // 主账号登录，有可能是子账号切换成主账号，要清除原子账号的数据
            sessionUser.setSubNick(null);
            sessionUser.setSubSessionId(null);
            sessionUser.setSubSessionTime(null);
            sessionUser.setSubExpiresIn(0);
            sessionUser.setSubExpiresInW2(0);
        }
        
        // 添加或更新用户信息
        String lastLoginTime = user.getLastLoginTime();
        user.setUserStatus(0);// 正常
        user.setLastLoginTime(datetime);
        
        // 用户数据保存到数据库
        if (isCreate)
            UserDao.addUser(user);
        else {
            
            Updater updater = new Updater();
            updater.addMust("nick", nick);
            updater.addField("sessionId", sessionToken.getSessionId());
            updater.addField("sessionTime", datetime);
            updater.addField("sessionToken", sessionToken.getToken());
            updater.addField("expiresIn", sessionToken.getExpiresIn());
            updater.addField("expiresInR1", sessionToken.getExpiresInR1());
            updater.addField("expiresInR2", sessionToken.getExpiresInR2());
            updater.addField("expiresInW1", sessionToken.getExpiresInW1());
            updater.addField("expiresInW2", sessionToken.getExpiresInW2());
            updater.addField("refreshToken", sessionToken.getRefreshToken());
            updater.addField("reExpiresIn", sessionToken.getReExpiresIn());
            updater.addField("userStatus", 0);
            updater.addField("lastLoginTime", datetime);
            if (!sessionToken.hasSubNick()) {
                if (StrUtils.isBlank(user.getOpenUid())) {
                    updater.addField("openUid", sessionToken.getOpenUid());
                }
                
                if (StrUtils.isBlank(user.getUserId())) {
                    updater.addField("userId", sessionToken.getUserId());
                }
            }
            
            if (!Validates.isDate(user.getEndDate()) || DateTimes.compareDay(DateTimes.getDateString(), user.getEndDate()) > 0) {
                updater.addField("endDate", DateTimes.getNextDateStringBySecond(sessionToken.getExpiresIn()));
            }
            UserDao.updateUser(nick, updater);
        }
        
        // 保存本次登录时间后，再显性地把真实的上次登录时间还原回来,注意lastLoginTime为空表示第一次登录
        user.setLastLoginTime(lastLoginTime);
        user.setOpenUid(sessionToken.getOpenUid());// 设置openUid
        sessionUser.setAdmin(false);
        sessionUser.setUser(user);
        
        // 第五步，检查赠送
        LoginAction.chkGift(sessionUser, user.getNick(), user.getVersionNo());
        
        // 检查appKey接入用户
        SysApp app = null;
        String sub_state = "";
        if (Strings.startsWith(state, "app")) {//
            long appKey = -1;
            String appKey_str = Strings.trimLeft(state, "app");
            int ind = appKey_str.indexOf("-");// 获取第三方参数回传
            if (ind != -1) {
                // 先获取 获取第三方参数回传
                sub_state = appKey_str.substring(ind + 1);
                
                // 在解析参数
                appKey_str = appKey_str.substring(0, ind);
            }
            
            if (Validates.isInteger(appKey_str))
                appKey = Long.parseLong(appKey_str);
            
            if (appKey > -1)
                app = ORM.get(ZTable.class).item(SysApp.class, new Selector("appKey", appKey));
            
            if (app == null || app.getAppStatus() == 1) {// 重定向
                request.setForward("/open-app-login-err.htm");
                return;
            }
            
            // 验证成功 插入关系用户
            UserDao.addAppExpandUser(appKey, nick);
            
            // 判断app是否允许开启TMC,不允许开启TMC则需要加入黑名单
            if (!app.isAllowOpenTmc())
                UserDao.addBlackUser(nick, Strings.format("app[%s][%s]扩展用户[%s],app 不允许开启消息，因此加入黑名单", appKey, app.getAppName(), nick));
        }
        
        // 获取跳转地址
        String redirect_url = request.getParameterNoFilter("redirect_url");
        if (Validates.isNotEmptyBlank(redirect_url)) {
            request.getResponse().sendRedirect(redirect_url);
        }
        else {
            // 最后检查状态位，内部重定向到该地址
            String forwardTo = getForwardUrl(sessionUser, state);
            
            // 这只第三方接入登录APPKey
            if (app != null) {// 直接跳转登录商家授权成功页
                sessionUser.setOpenAppKey(app.getAppKey());
                forwardTo = "/open-app-login-succ.htm?id=" + app.getAppKey() + "&sub_state=" + sub_state;
            }
            request.forwardTo(forwardTo);
        }
    }
    
    /** 是否有错误信息 */
    protected boolean hasError(HttpRequest request, HttpResponse response) throws IOException {
        String error = request.getParameter("error", null);
        String error_description = request.getParameter("error_description", null);
        
        if (Validates.isNotEmpty(error) || Validates.isNotEmpty(error_description)) {
            log.error("读取OAuth时失败:error=" + error);
            int versionNo = Global.getInt("taobao", "orderVersionNo", -1);
            
            if (versionNo > 0)
                response.sendRedirect("/ycTaoOrder.htm?versionNo=" + versionNo);
            else
                response.sendRedirect(fuwuUrl);
            
            return true;
        }
        
        return false;
    }
    
    /** 是否检测检查 */
    protected boolean isTaobaoChk(HttpRequest request, HttpResponse response) throws IOException {
        String code = request.getParameter("code", null);
        String state = request.getParameterNoFilter("state", "");
        String userAgent = request.getHeader(_USER_AGENT_);
        if (Validates.isEmpty(state) && (Validates.isEmpty(userAgent) || userAgent.indexOf("(X11; Linux i686)") != -1)) {
            log.info("淘宝测试，code=" + code + ";state=" + state + ";userAgent=" + userAgent);
            response.sendRedirect(fuwuUrl);
            return true;
        }
        
        return false;
    }
    
    /** 获取版本 */
    protected int parseVersionNo(String state) {
        int ind1 = state.indexOf("versionNo:");
        if (ind1 == -1)
            return 0;
        
        int ind2 = state.indexOf(";", ind1);
        if (ind2 == -1)
            return 0;
        
        String versionStr = state.substring(ind1 + "versionNo:".length(), ind2);
        int versionNo = Integer.parseInt(versionStr);
        return versionNo;
    }
    
    /** 获取要求的最后转向 */
    protected String getForwardUrl(SessionUser sessionUser, String state) {
        if ("w2close".equals(state) || "w2close_index".equals(state) || "w2close_qianniupc".equals(state)) {// 如果是短授权，关闭
            return "/w2Resp.htm?state=" + state;
        }
        
        sessionUser.setFrom(0);
        return "/web.htm";
    }
    
    /**
     * 检查赠送
     * 
     * @param sessionUser 用户会话
     * @param nick 用户昵称
     * @param versionNo 版本号
     * @throws Exception 异常
     */
    public static void chkGift(SessionUser sessionUser, String nick, int versionNo) throws Exception {
        Selector selector = new Selector();
        selector.addMust("nick", nick);
        selector.addMaybe("status", 1);
        selector.addMaybeThenGE("versionNo", versionNo);
        selector.addMaybeThenG("endDate", DateTimes.getDateString());
        selector.addOrderbyDesc("endDate");
        UserGift userGift = ORM.table().item(UserGift.class, selector);
        if (userGift != null) {
            sessionUser.setGift(true);
            sessionUser.setGiftVersionNo(userGift.getVersionNo());
            sessionUser.setGiftStartDate(userGift.getBeginDate());
            sessionUser.setGiftEndDate(userGift.getEndDate());
        }
    }
}
